//
// Created by gastonfeng on 2022/11/3.
//

#include <ulp.h>
#ifdef USE_BLE_SERIAL

#include "BLE_PassPort.h"
#include <plc_rte.h>

void cb_led_master() {
  if (digitalRead(ble.led_master)) {
    ble.data.master = 0;
    ble.data.ble_connect = 0;
  }
  else {
    ble.data.master = 1;
    ble.data.ble_connect = 1;
  }
}

void cb_led_slave() {
  if (digitalRead(ble.led_slave)) {
    ble.data.slave = 0;
    ble.data.ble_connect = 0;
  }
  else {
    ble.data.slave = 1;
    ble.data.ble_connect = 1;
  }
}

int BLE_PassPort::begin(int rts, int cts, int led_slave, int led_master,
                        int rst) {
  this->rts = rts;
  this->cts = cts;
  this->led_slave = led_slave;
  this->rst = rst;

  pinMode(led_slave, INPUT);

  pinMode(rst, OUTPUT);
  digitalWrite(rst, LOW);
  Rtos::Delay(100);
  digitalWrite(rst, HIGH);
  Rtos::Delay(1000);
  pinMode(rts, INPUT);

  pinMode(cts, OUTPUT);
  digitalWrite(cts, LOW);
  kSerial::begin(38400, SERIAL_8N1);
  if (!is_AT_mode()) {
    enter_AT_mode();
    Rtos::Delay(200);
  }
  sendAT("AT+UART=", "115200");
  Rtos::Delay(2500);
  kSerial::data.baud = 115200;
  pinMode(led_master, INPUT);
  pinMode(led_slave, INPUT);
  attachInterrupt(led_master, &cb_led_master, CHANGE);
  attachInterrupt(led_slave, &cb_led_slave, CHANGE);
  return 0;
}

int BLE_PassPort::begin(uint32_t tick) {
  pinMode(rts, INPUT);

  pinMode(cts, OUTPUT);
  digitalWrite(cts, LOW);
  if (!is_AT_mode()) {
    enter_AT_mode();
    Rtos::Delay(200);
  }
  sendAT("AT+UART?", "");
  set_device_name(ble_name);
  Rtos::Delay(500);
  sleep(0, 1, 0);
  //   exit_AT_mode();
  Rtos::Delay(200);
  thd = Rtos::create_thread_run(name(), 1024, 1, (void *)&thread, this);
  return 0;
}

#ifdef __PLATFORMIO_BUILD_DEBUG__
int BLE_PassPort::dev_test(uint32_t) {
  // String name = get_device_name();
  // logger.debug("BLE device name: %s", name.c_str());
  // Rtos::pause_thread(thd);

  // while (true)
  // {
  //     if (!is_AT_mode())
  //     {
  //         if (0 == enter_AT_mode())
  //             break;
  //     }
  //     else
  //     {
  //         if (0 == exit_AT_mode())
  //             break;
  //     }
  //     Rtos::Rtos::Delay(1000);
  // }
  // Rtos::resume_thread(thd);

  return 0;
}
#endif

int BLE_PassPort::diag(uint32_t) { return 0; }

int BLE_PassPort::run(uint32_t) {
  if (data.ble_connect != last_state) {
    data.mode = pb_state_Stopped;
    last_state = data.ble_connect;
    vUlpOnPeripheralsActive(ulpPERIPHERAL_BLE);
    wakeup();
    Rtos::Delay(200);
    if (!is_AT_mode()) {
      enter_AT_mode();
      Rtos::Delay(200);
    }
    if (!is_AT_mode()) {
      enter_AT_mode();
      Rtos::Delay(200);
    }
    if (data.ble_connect) {
      Rtos::Delay(200);
      //   enter_AT_mode();
      sleep(1, 1, 0);
      Rtos::Delay(200);
      exit_AT_mode();
      logger.info("ble connect");
    }
    else {
      Rtos::Delay(200);
      // enter_AT_mode();
      // Rtos::Delay(200);
      sleep(0, 1, 0);
      Rtos::Delay(200);
      //   exit_AT_mode();
      logger.info("ble disconnect");
      vUlpOnPeripheralsInactive(ulpPERIPHERAL_BLE);
      digitalWrite(cts, HIGH);
    }
    data.mode = pb_state_Started;
  }
  return 0;
}

void BLE_PassPort::wakeup() const {
  pinMode(cts, OUTPUT);
  digitalWrite(cts, LOW);
  Rtos::Delay(10);
  digitalWrite(cts, HIGH);
  Rtos::Delay(1000);
  digitalWrite(cts, LOW);
  Rtos::Delay(10);
}

String BLE_PassPort::sendAT(String cmd, String param) {
  digitalWrite(cts, LOW);

  while (available()) {
    read();
  }
  while (!availableWrite()) {
  }

  cmd += param + "\r\n";
  size_t cmdLen = cmd.length();
  kSerial::write((const u8 *)cmd.c_str(), cmdLen);
  Rtos::Delay(500);
  int len = 0;
  char recvChar;
  String recvBuf;
  if (len = available()) {
    for (int i = 0; i < len; i++) {
      recvChar = read();
      recvBuf += recvChar;
    }
    logger.debug(recvBuf.c_str());
  }
  return recvBuf;
}

// && ((digitalRead(led_slave) ? 0 : 1) || (digitalRead(led_master) ? 0 : 1)
int BLE_PassPort::availableWrite() { return (digitalRead(rts) ? 0 : 1); }

int BLE_PassPort::set_device_name(const char* name) {
  String recvBuf = sendAT("AT+NAME=0,", name);
  if (recvBuf.indexOf("OK") >= 0) {
    logger.debug("set ble name success");
  }
  else {
    logger.error("set ble name fail");
  }

  return 0;
}

String BLE_PassPort::get_device_name() { return sendAT("AT+NAME?", ""); }

int BLE_PassPort::enter_AT_mode() {
  digitalWrite(cts, LOW);
  while (!availableWrite()) {
  }
  Rtos::Rtos::Delay(20);
  kSerial::write((const u8 *)"+++", 3);
  // Rtos::Rtos::Delay(20);
  // write('+');
  // Rtos::Rtos::Delay(20);
  // write('+');
  os.Rtos::Delay(200);
  int len = 0;
  char recvChar;
  String recvBuf;
  if (len = available()) {
    for (int i = 0; i < len; i++) {
      recvChar = read();
      recvBuf += recvChar;
    }
    logger.debug(recvBuf.c_str());
  }
  if (recvBuf.indexOf("OK") >= 0) {
    logger.debug("enter ble AT mode success");
    return 0;
  }
  else {
    logger.error("enter ble AT mode fail : %s", recvBuf.c_str());
  }
  return -1;
}

int BLE_PassPort::exit_AT_mode() {
  String recvBuf = sendAT("AT+EXIT", "");
  if (recvBuf.indexOf("OK") >= 0) {
    logger.debug("exit ble AT mode success");
  }
  else {
    logger.error("exit ble AT mode fail");
  }
  return 0;
}

int BLE_PassPort::sleep(bool port_en, bool ble_en, bool warkup_en) {
  logger.debug("sleep: %d, %d, %d", port_en, ble_en, warkup_en);
  String param =
      String(port_en) + "," + String(ble_en) + "," + String(warkup_en);
  logger.debug("param: %s", param.c_str());
  assert(param.length() == 5);
  String recvBuf = sendAT("AT+SLEEP=", param);
  logger.debug("recvBuf: %s", recvBuf.c_str());
  if (recvBuf.length() > 16) {
    logger.error("sleep fail: %d", recvBuf.length());
    return -1;
  }
  if (recvBuf.indexOf("OK") >= 0) {
    logger.debug("set ble sleep success");
  }
  else {
    logger.error("set ble sleep fail");
  }
  // digitalWrite(cts, HIGH);
  return 0;
}

[[noreturn]] void BLE_PassPort::thread(void* arg) {
  auto* p = (BLE_PassPort *)arg;
  while (true) {
    if (p->data.mode != pb_state_Started) {
      Rtos::Delay(1000);
      continue;
    }
    p->firm.loop(p);
  }
}

bool BLE_PassPort::is_AT_mode() {
  String recvBuf = sendAT("AT+ECHO=0", "");
  if (recvBuf.indexOf("OK") >= 0) {
    logger.debug(" ble AT mode ");
    return true;
  }
  else {
    logger.error(" ble not AT mode ");
    return false;
  }
}

int BLE_PassPort::end() {
  pinMode(rts, INPUT_PULLUP);
  pinMode(cts, INPUT_PULLUP);
  return kSerial::end();
}

BLE_PassPort ble;
#endif
